home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Interactive Reference Guide / C-C++ Interactive Reference Guide.iso / c_ref / csource4 / 210_01 / kittylnt.c < prev    next >
Text File  |  1985-11-15  |  22KB  |  716 lines

  1. /*lintoff*/
  2. /* Saturday, July 12, 1986  3:41 PM                                  */
  3. /*                                                                   */
  4. /*     KITTENSOFT                                                    */
  5. /*                                                                   */
  6. /*     Dan Schechter                                                 */
  7. /*     Route 1 Box 19                                                */
  8. /*     Amenia, North Dakota 58004                                    */
  9. /*     (701) 967-8371                                                */
  10. /*                                                                   */
  11. /*     Do what you like with this program. Please let me know if     */
  12. /*     you find bugs. Exactly as is it compiles under Aztec C II     */
  13. /*     for CP/M. I have no idea how portable it is because I do      */
  14. /*     not know how compatible the Manx library functions are with   */
  15. /*     those of other compilers.                                     */
  16. /*                                                                   */
  17. /*     This is not intended to be a real LINT program. That would    */
  18. /*     be far beyond my programming ability. This is just a sort     */
  19. /*     of delimiter checker. I have used it only for a short time    */
  20. /*     but I have found it useful. I hope you will also.             */
  21. /*                                                                   */
  22. /*     Following are some general comments about this program:       */
  23. /*
  24. When checking for proper line termination it returns <good> on
  25. end-of-comment marker. This might not be the best choice as it could mask
  26. an error on the same line (when a short comment is on the same line as
  27. a line of code).
  28.  
  29. It expects if, for, and while loops to be terminated by closing parenthesis
  30. (no semicolon) if and only if the loop declaration has no following statement.
  31.  
  32.     Example 1:    if (a==b)
  33.                 x=y;
  34.  
  35.     Example 2:    if (a==b) x=y;
  36.  
  37. Both the above are correct and will not cause error messages.
  38.  
  39.     Example 3:    while(a[i++]!=0);
  40.  
  41. In the above, the programmer wants to increment i until a[i] is 0,
  42. thus the semicolon is correct, but the lint program expects any kind of
  43. loop to control a statement or block of statements, and so generates
  44. the error message "Check line termination".
  45.  
  46. Sometimes, line labels will generate spurious error messages.
  47.  
  48. It will check printf(), fprintf(), sprintf and scanf() to see that the number
  49. of parameters matches the number of <%> conversion characters. However it
  50. does NOT check for type consistency, or anything about parameters 
  51. passed to functions. It will check to make sure that a for() loop has
  52. exactly two semicolons inside the parentheses. A spurious error message
  53. will be generated if a function name ends in "for" as the program thinks
  54. it is a for() loop.
  55.  
  56. It will check for the balance of () parentheses, [] brackets, "" quote marks, 
  57. and {} braces. Spurious error messgaes may be generated if any of these are
  58. used as text characters. Parentheses, braces, and quotes, but not brackets,
  59. may be balanced on following lines if the LAST character on a line is a
  60. backslash. Even white space following a backslash is not permitted. This is
  61. consistent with Aztec C. This program will flag white space following the 
  62. last backslash on a line.
  63.  
  64. It will flag a line that appears to be a function declaration with a
  65. semicolon. This may generate some spurious error messages.
  66.  
  67. It will flag nested or unbalanced comment markers.
  68.  
  69. It will not check for undeclared variables.
  70.  
  71. It does not recognize the comma that terminates lines in initializing
  72. statements as permissable. Thus initializers will generate spurious error
  73. messages. I suggest toggling lint off for these. 
  74.  
  75. Most of the time it will find the syntax mistakes I make most often:
  76. unbalanced parentheses, brackets, braces, and quotes; missing semicolons,
  77. semicolons after function declarations, nested comments, parameters
  78. left out of printf() statements, and too many or two few semicolons
  79. inside for() statements.
  80.  
  81. Checking is toggled on and off with the words                         */
  82. /*linton*/
  83. /*lintoff*/                                                           /*
  84. which must appear starting at column 1, between comment markers,
  85. without spaces. They may not be nested.
  86.                                                                       */
  87. /*linton*/
  88.  
  89. #include <stdio.h>
  90. #define BELL 7
  91. int errno;
  92. main(n,arg)
  93. int n;
  94. char **arg;
  95. {
  96.     int i,op=0,cp=0,ob=0,cb=0,os=0,cs=0,cmt=0,sq=0,quo=0,quot=0,l=0,conline=0;
  97.     char disab=0,q,string[300],fn[20],*fgets(),*r=1,cmtb=0;
  98.     FILE *fp;
  99.  
  100. /* If there is a file name on the command line it will use it. Otherwise */
  101. /* it will ask for a file name                                           */
  102.  
  103.     if (n==2) strcpy(fn,arg[1]);
  104.     else getfn(fn);
  105.  
  106. o:    if ((fp=fopen(fn,"r"))==0){
  107.         printf("Can't find %s  errno=%d",fn,errno);
  108.         errno=0;
  109.         getfn(fn);
  110.         goto o;
  111.     }
  112.     
  113.     while(r){
  114.         l++;
  115.  
  116. /* The following initializes string to all newline characters. Without this, */
  117. /* under rare circumstances, the last line can get spurious error flags as   */
  118. /* the string winds up with part of the next-to-last line hanging on.        */
  119.  
  120.         for (i=0;i<255;i++) string[i]='\n';
  121.  
  122. /* The following gets one line of source code */
  123.  
  124.         r=fgets(string,250,fp);
  125.  
  126. /* It ignores lines beginning with # because they usually require no */
  127. /* special line termination:                                         */
  128.  
  129.         if (string[0]=='#') continue;
  130.         if ((i=strncmp(string,"/*lintoff*/",11))==0) {
  131.             disab=1;
  132.  
  133. /* conline keeps track of how many lines have been printed, so the */
  134. /* program can pause for you to read the error messages.           */
  135.  
  136.             conline++;
  137.             printf("\
  138. %s line: %d --> LINT TURNED OFF.\n\n",fn,l);
  139.             if (conline>9) pause(&conline);
  140.         }
  141.         if ((i=strncmp(string,"/*linton*/",10))==0) {
  142.             disab=0;
  143.             conline++;
  144.             printf("\
  145. %s line: %d --> LINT TURNED BACK ON.\n\n",fn,l);
  146.             if (conline>9) pause(&conline);
  147.         }
  148.         if (disab) continue;
  149.         q=cktsp(string);
  150.         if (q=='b'){
  151.             conline++;
  152.             printf("\
  153. %s line: %d --> White space after backslash:\n%s",fn,l,string);
  154.             q='r';
  155.             if (conline>9) pause(&conline);
  156.         }
  157.         diddle(string);
  158.  
  159. /* This is to skip blank lines:  */ 
  160.  
  161.         if (string[0]=='\n') continue;
  162.         q=endcheck(string);    
  163.         if (  (q=='f') && ((op!=cp)||(ob!=cb))  ) {
  164.             conline++;
  165.             printf("\
  166. %s line: %d --> New function declaration. --> %s\
  167. Unbalanced parentheses<%d> or wavy brackets<%d> in the preceding function.\n",\
  168. fn,l,string,(op-cp),(ob-cb));
  169.             if (conline>9) pause(&conline);
  170.             op=0;cp=0;ob=0;cb=0;os=0;cs=0;
  171.         }
  172.         if (q=='b'){
  173.             q=endexcpt(string);
  174.             if (q=='b'){                     
  175.                 conline++;
  176.                 printf("\
  177. %s line: %d --> check line termination:\n%s",fn,l,string);
  178.                 if (conline>9) pause(&conline);
  179.             }                                
  180.             q='r';
  181.         }
  182.         if (q=='g'){
  183.             q=loopwscn(string);
  184.             if (q=='b'){                     
  185.                 conline++;
  186.                 printf("\
  187. %s line: %d --> check line termination:\n%s",fn,l,string);
  188.                 if (conline>9) pause(&conline);
  189.             }                                
  190.             q='r';
  191.         }
  192.  
  193. /* These keep track of brackets: */
  194.  
  195.         for (i=0;string[i]!='\n';i++){
  196.             if (string[i]=='(') op++;
  197.             if (string[i]==')') cp++;
  198.             if (string[i]=='{') ob++;
  199.             if (string[i]=='}') cb++;
  200.             if (string[i]=='[') os++;
  201.             if (string[i]==']') cs++;
  202.  
  203. /* This keeps track of comment markers: */
  204.  
  205.             if (string[i]=='*'){
  206.                 if (string[i-1]=='/') cmt++;
  207.                 if (string[i+1]=='/') cmt--;
  208.                 if ((cmt>1)||(cmt<0)) cmtb=1; 
  209.             }
  210.  
  211. /* These keep track of apostrophes and quotes: */
  212.  
  213.             if (string[i]=='\'') sq^=1;
  214.             if (string[i]=='\"') {
  215.                 quo^=1;
  216.                 quot++;
  217.             }
  218.         }
  219.         if (os!=cs) {
  220.             conline++;
  221.             printf("\
  222. %s line: %d --> unbalanced square brackets:\n%s",fn,l,string);
  223.             if (conline>9) pause(&conline);
  224.         }
  225.         os=0;
  226.         cs=0;
  227.         if (op!=cp) {
  228.             q=querybks(string);
  229.             if (q=='b') {
  230.                 conline++;
  231.                 printf("\
  232. %s line: %d --> unbalanced parentheses:\n%s",fn,l,string);
  233.                 op=0;cp=0;
  234.                 if (conline>9) pause(&conline);
  235.             }
  236.         }
  237.         if (cmtb){
  238.             conline++;
  239.             printf("\
  240. %s line: %d --> Nested comments or close comment first:\n%s",fn,l,string);
  241.             cmt=0;
  242.             cmtb=0;
  243.             if (conline>9) pause(&conline);
  244.         }
  245.  
  246. /* There are two counters for quote marks. quo is used to check for */
  247. /* unterminated strings. quot is used in testing printf() etc. for  */
  248. /* parameter number. It tells the program when to begin and when to */
  249. /* stop counting conversion characters.                             */
  250.  
  251.         if (quot) {
  252.             if ((q=paramck(string))=='b'){
  253.                 conline++;
  254.                 printf("\
  255. %s line: %d --> Unbalanced parameter/conversion number in printf:\n%s",fn,l,string);
  256.                 if (conline>9) pause(&conline);
  257.                 quot==0;
  258.             }
  259.             if (quot=='g') quot=0;
  260.         }
  261.         if (quo==1) {
  262.             q=querybks(string);
  263.             if (q=='b'){
  264.                 conline++;
  265.                 printf("\
  266. %s line: %d --> Unterminated string:\n%s",fn,l,string);
  267.                 quo=0;
  268.                 if (conline>9) pause(&conline);
  269.             }
  270.         }
  271.         if (sq==1){
  272.             printf("\
  273. %s line %d --> Unmatched apostrophe:\n%s",fn,l,string);
  274.             conline++;
  275.             if (conline>9) pause(&conline);
  276.             sq=0;
  277.         }
  278.         q=funcdecl(string);
  279.         if (q=='b') {
  280.             conline++;
  281.             printf("\
  282. %s line: %d --> Semicoln after function declaration:\n%s",fn,l,string);
  283.             if (conline>9) pause(&conline);
  284.         }
  285.         q=sminfor(string);
  286.         if (q=='b') {
  287.             conline++;
  288.             printf("\
  289. %s line: %d --> Improper number of arguments in for(;;) statement:\n%s",fn,l,string);
  290.             if (conline>9) pause(&conline);
  291.         }
  292.     }
  293.     if (ob!=cb) {
  294.         conline++;
  295.         printf("\n\
  296. %s --> Unbalanced wavy brackets.",fn);
  297.         if (conline>9) pause(&conline);
  298.     }
  299.     if (cmt) {
  300.         conline++;
  301.         printf("\n\
  302. %s --> Unbalanced comment markers.",fn);
  303.         if (conline>9) pause(&conline);
  304.     }
  305.  
  306.     if (errno) {
  307.         conline++;
  308.         printf("\nThere was a file error. errno=%d",errno);
  309.         if (conline>9) pause(&conline);
  310.     }
  311.     fclose(fp);
  312.     printf("\n%cDone. Would you like to check another file? ",BELL);
  313.     q=getreply(2,"yn");
  314.     if (q=='n') exit();
  315.     getfn(fn);
  316.     op=0;cp=0;ob=0;cb=0;os=0;cs=0;l=0;r=1;conline=0,cmtb=0;
  317.     goto o;
  318. }
  319.  
  320. /* There are two versions of the following function getfn(). The first    */
  321. /* is for CP/M. I like it because it lets me use Aztec C's T.LIB, which   */
  322. /* results in much smaller compiled programs. It also seems to run better */
  323. /* on my system.                                                          */
  324.  
  325. /* The second version uses fgets() instead of bdos(10) and so ought to be */
  326. /* portable, I hope. I have left the first version "commented" OFF and    */
  327. /* so the second version will be selected as this stands. You may want to */
  328. /* try the first version if you have a CP/M system.                       */
  329.  
  330. getfn(name)
  331. char name[];
  332. {
  333.     char i;
  334.     name[0]=18;
  335.     printf("\nPlease enter file name. ");
  336.     bdos(10,name);
  337.     printf("\n");
  338.     name[name[1]+2]=0;
  339.     for (i=0;i<18;i++) name[i]=name[i+2];
  340.     for (i=0;name[i]!=0;i++) if (name[i]>96) name[i]-=32;
  341. }
  342.  
  343. /*
  344. getfn(name)
  345. char name[];
  346. {
  347.     char i,*p,*fgets();
  348.     printf("\nPlease enter file name. ");
  349.     do { p=fgets(name,17,stdin); }
  350.     while (name[0]=='\n');
  351.     for (i=0;name[i]!='\n';i++);
  352.     name[i]=0;
  353.     for (i=0;name[i]!=0;i++) if (name[i]>96) name[i]-=32;
  354. }
  355.                                                                         */
  356. /* The following function returns 'g' for good if the last character */
  357. /* on the line is a backslash.                                       */
  358.  
  359. querybks(s)
  360. char s[];
  361. {
  362.     char *p,*index();
  363.     p=index(s,'\n');
  364.     if (p==0) return('e');
  365.     p--;
  366.     if (*p=='\\') return('g');
  367.     return ('b');
  368. }
  369.  
  370. /* This one returns 'g' for good if there is a proper terminating character  */
  371. /* at the end of the line. If the last character is a close parenthesis it   */
  372. /* returns good if it appears to be a function declaration, as that does not */
  373. /* want a normal line termination character.                                 */
  374.  
  375. endcheck(s)
  376. char s[];
  377. {
  378.     char *p,*index(),q;
  379.  
  380.     p=index(s,'\n');
  381.     if (p==0) return('e');
  382.     p--;
  383.     if (*p=='\\') return('g');
  384.     if (*p=='{') return('g');
  385.     if (*p=='}') return('g');
  386.     if (*p==';') return('g');
  387.     if ((*p=='/')&&(*(p-1)=='*')) return('g');
  388.     if (*p==')') {
  389.         q=askfunc(s);
  390.         if (q=='y') return('f');
  391.     }
  392.  
  393. /* The following returns 'g' if the only thing on the line is a begin-  */
  394. /* comment marker, starting at column 1;                                */
  395.  
  396.     if ((p-s==1)&&(*(p)=='*')&&(*(p-1)=='/')) return('g');
  397.     return('b');
  398. }
  399.  
  400. /* This tries to determine if the line is a function declaration, and */
  401. /* returns 'b' for bad if it appears to be a function declaration     */
  402. /* with a semicolon after it. I included this because I often hit the */
  403. /* semicolon key by reflex when it is not called for. However this    */
  404. /* function does cause some spurious error messages. It returns 'b'   */
  405. /* if column 1 contains a lower case alphabetic character, and there  */
  406. /* is an open parenthesis within the first 10 characters, and the     */
  407. /* line is terminated with a semicolon.                               */
  408.  
  409. funcdecl(s)
  410. char s[];
  411. {
  412.     char *p,*index();
  413.     int q;
  414.     p=index(s,'\n');
  415.     if (p==0) return('e');
  416.     if (*(--p)==';')
  417.         if (*(p-1)==')')
  418.             if ((s[0]>96)&&(s[0]<123)){
  419.                 p=index(s,'(');
  420.                 if (p==0) return('g');
  421.  
  422. /* The 8 in the following line is because Aztec C limits labels to a   */
  423. /* maximun of 8 characters. Thus anything longer is not likely to be   */
  424. /* a function name. You may wish to change this if your system allows  */
  425. /* longer function names. However the longer it is the more likely it  */
  426. /* is to generate spurious error messages.                             */
  427.  
  428.                 if ((p-s)>8) return('g');
  429.                 return('b');
  430.             }
  431.     return('g');
  432. }
  433.  
  434. /* This is called if there is no "normal" terminating character at the  */
  435. /* end of a line. It returns 'g' for good if the line is an "if", "for" */
  436. /* or "while" loop. See also loopwscn() below.                          */
  437.  
  438. endexcpt(s)
  439. char s[];
  440. {
  441.     char a=0,i=0;
  442.     int q,parct=0,pmax=0;
  443.     
  444.     if (s[0]=='\n') return('g');
  445.     while (s[i]=='\t') i++;
  446.     if ((q=strncmp(s+i,"if",2))==0) a=1;
  447.     if ((q=strncmp(s+i,"for",3))==0) a=1;
  448.     if ((q=strncmp(s+i,"while",5))==0) a=1;
  449.     if (a==0) return('b');
  450.     
  451.     for (i=0;s[i]!='\n';i++){
  452.         if (s[i]=='(') parct++;
  453.         if (s[i]==')') parct--;
  454.         if (parct<0) return('b');
  455.         if (pmax<parct) pmax=parct;
  456.         if ((parct==0)&&(pmax>0)) break;
  457.     }
  458.     if (s[++i]=='\n') return('g');
  459.     return('b');
  460. }
  461.  
  462. /* This is another CP/M bdos call. I like it better than getchar() because */
  463. /* I can use this one and still compile the program with the t.lib, which  */
  464. /* makes for more compact programs. Also, on my compiler getchar()         */
  465. /* requires you to press <RETURN>, whereas bdos(1,0) does not.             */
  466. /*                                                                         */
  467. /* The second version of this function uses getchar() instead, and so      */
  468. /* should be portable. I have left the portable version ON and the CP/M    */
  469. /* version OFF, as before. You may want to change that if you have CP/M.   */
  470.  
  471. getreply(n,s)
  472. int n;
  473. char s[];
  474. {
  475.     char c,i;
  476.     for(;;){
  477.         c=bdos(1,0);
  478.         if ((c>64)&&(c<91)) c+=32;
  479.         for (i=0;i<n;i++) if (c==s[i]) return(c);
  480.     }
  481. }
  482.  
  483. /*
  484. getreply(n,s)
  485. int n;
  486. char s[];
  487. {
  488.     char c,i;
  489.     for(;;){
  490.         c=getchar();
  491.         if ((c>64)&&(c<91)) c+=32;
  492.         for (i=0;i<n;i++) if (c==s[i]) return(c);
  493.     }
  494. }
  495.                                                                      */
  496. /* This one just deletes white space from the end of the line for */
  497. /* the convenience of other functions.                            */
  498.  
  499. diddle(s)
  500. char s[];
  501. {
  502.     char i=0,q=0;
  503.     for (;s[i]!='\n';i++){
  504.         if (s[i]=='\t') continue;
  505.         if (s[i]==' ') continue;
  506.         q=1;
  507.     }
  508.     if (q==0) {
  509.         s[0]='\n';
  510.         s[1]=0;
  511.         return;
  512.     }
  513.     i--;
  514.     while((s[i]==' ')||(s[i]=='\t')) i--;
  515.     s[++i]='\n';
  516.     s[++i]=0;
  517. }
  518.  
  519. /* This pauses operation when the screen has been filled with error     */
  520. /* messages, and gives you a chance to read them and then continue      */
  521. /* or exit the program. It also resets conline by means of the pointer. */
  522.  
  523. pause(a)
  524. int *a;
  525. {
  526.     char q;
  527.     printf("\n%cPaused. Would you like to continue? ",BELL);
  528.     q=getreply(2,"yn");
  529.     if (q=='n') exit();
  530.     *a=0;
  531.     printf("\n");
  532. }
  533.  
  534. /* This is the one that checks for white space following a backslash */
  535. /* as mentioned in the introduction. If there is more than one it    */
  536. /* looks only at the last one.                                       */
  537.  
  538. cktsp(s)
  539. char s[];
  540. {
  541.     char *p,*rindex();
  542.     p=rindex(s,'\\');
  543.     if (p==0) return('g');
  544.     p++;
  545.     if ((*p==' ')||(*p=='\t')) return('b');
  546.     return('g');
  547. }
  548.  
  549. /* This one returns 'y' if it thinks the line is a function declaration. */
  550. /* It is not always right, so it causes some spurious error messages.    */
  551. /* But I still like to have it.                                          */
  552.  
  553. askfunc(s)
  554. char s[];
  555. {
  556.     char *p,*index();
  557.     if ((s[0]>96)&&(s[0]<123)){
  558.         p=index(s,'(');
  559.         if (p==0) return('n');
  560.         if ((p-s)>8) return('n');
  561.         return('y');
  562.     }
  563.     return('n');
  564. }
  565.  
  566. /* This function is the one that decides whether an "if" "for" or    */
  567. /* "while" statement is properly terminated. As mentioned above,     */
  568. /* it will flag a semicolon as an error if there is no other         */
  569. /* statement on the same line. See the examples in the introduction. */
  570.  
  571. loopwscn(s)
  572. char s[];
  573. {
  574.     char a=0,i=0;
  575.     int q,parct=0,pmax=0;
  576.     
  577.     if (s[0]=='\n') return('g');
  578.     while (s[i]=='\t') i++;
  579.     if ((q=strncmp(s+i,"if",2))==0) a=1;
  580.     if ((q=strncmp(s+i,"for",3))==0) a=1;
  581.     if ((q=strncmp(s+i,"while",5))==0) a=1;
  582.     if (a==0) return('g');
  583.     
  584.     for (i=0;s[i]!='\n';i++){
  585.         if (s[i]=='(') parct++;
  586.         if (s[i]==')') parct--;
  587.         if (parct<0) return('b');
  588.         if (pmax<parct) pmax=parct;
  589.         if ((parct==0)&&(pmax>0)) break;
  590.     }
  591.  
  592.     while ((s[++i]==' ')||(s[i]=='\t'));
  593.     if (s[i]==';') return('b');
  594.     return('g');
  595. }
  596.  
  597. /* This will be passed lines that have quote marks in them. First it       */
  598. /* looks for printf, sprintf, fprintf, or scanf and aborts if one is       */
  599. /* not found, as its job is just to check those functions. At the first    */
  600. /* quote mark it begins counting up % conversion characters. At the second */
  601. /* quote mark it stops counting those and starts counting commas, until    */
  602. /* the end of the line. If the two figures are equal it returns 'g' for    */
  603. /* good, otherwise 'b' for bad. Also it returns 'b' if there is a comma    */
  604. /* immediately before the final close parenthesis, as that suggests an     */
  605. /* empty field. If all goes well it will generate an error message when the */
  606. /* number of parameters does not match the number of % characters.         */
  607. /* If the line is terminated with a backslash all the counters are held    */
  608. /* static and the count continues on the next line. This function does not */
  609. /* check for unbalanced parentheses, as that is flagged elsewhere.         */
  610.  
  611. paramck(s) 
  612. char s[];
  613. {
  614.     char q,i=0;
  615.     static int convct,paramct;
  616.     static char a=0;
  617.  
  618. /* The following direct execution to the proper place in the function */
  619. /* when the count has been carried over from a previous line.         */
  620.  
  621.     if (a==4) goto A4; 
  622.     if (a==3) goto A3; 
  623.     if (a==2) goto A2;
  624.     if (a==1) printf("<a> error in paramck()\n");
  625.     if (a>4) printf("<a> error in paramck()\n");
  626.     convct=0 ; paramct=0;
  627.     
  628.  
  629. /* The following will find the key words printf, scanf, fprintf, or sprintf */
  630.  
  631.     for (i=0;s[i]!='\n';i++)
  632.         if ((q=strncmp(s+i,"printf",6))==0) a=1;
  633.         if ((q=strncmp(s+i,"scanf",5))==0) a=1;
  634.     if (a==0) return('g');
  635.  
  636. /* The following counts up the number of % characters */
  637.  
  638. A2:    for (i=0;s[i]!='\n';i++) 
  639.         if (s[i]=='%'){
  640.             convct++;
  641.             i++;
  642.             break;
  643.         }
  644.     if ((s[i]=='\n')&&(s[i-1]=='\\')){
  645.         a=2;
  646.         return('c');
  647.     }
  648.     if (s[i]=='\n') {
  649.         a=0;
  650.         return('g');
  651.     }
  652. A3:    for (;(s[i]!='\"')&&(s[i]!='\n');i++) if (s[i]=='%') convct++;
  653.     if ((s[i]=='\n')&&(s[i-1]=='\\')){
  654.         a=3;
  655.         return('c');
  656.     }
  657.     if (s[i]!='\"') {
  658.         a=0;
  659.         return('g');
  660.     }
  661.  
  662. /* This counts up the number of commas after the closing quotes. */
  663.  
  664. A4:    for (;(s[i]!='\n')&&(s[i]!=';');i++) if (s[i]==',') paramct++;
  665.     if ((s[i]=='\n')&&(s[i-1]=='\\')){
  666.         a=4;
  667.         return('c');
  668.     }
  669.     if (s[i]!=';') {
  670.         a=0;
  671.         return('g');
  672.     }
  673.  
  674. /* Now it re-sets the place marker and checks the counters for equality */
  675.  
  676.     a=0;
  677.     if (convct!=paramct) return('b');
  678.  
  679. /* This steps backward from the end of the line past white space and */
  680. /* parentheses and then looks for a comma. This is to make sure that */
  681. /* the last field actually contains something.                       */
  682.  
  683.     while ((s[--i]==')')||(s[i]==' ')||(s[i]=='\t')||(s[i]=='('));
  684.     if (s[i]==',') return('b');
  685.     return('g');
  686. }
  687.  
  688. /* This counts up the number of semicolons in a for(;;) loop. */
  689.  
  690. sminfor(s)
  691. char s[];
  692. {
  693.     char i;
  694.     int q,parct=0,pmax=0,ct=0;
  695.     
  696.     if (s[0]=='\n') return('g');
  697.  
  698.     for (i=0;s[i]!=0;i++) if ((q=strncmp(s+i,"for",3))==0) break;
  699.     if (s[i]==0) return('g');
  700.     
  701.     for (i+=3;(s[i]==' ')||(s[i]=='\t');i++);
  702.     if (s[i]=='(') parct++;
  703.     else return('g');
  704.     
  705.     for (++i;s[i]!='\n';i++){
  706.         if (s[i]=='(') parct++;
  707.         if (s[i]==')') parct--;
  708.         if (s[i]==';') ct++;
  709.         if (parct<0) return('b');
  710.         if (pmax<parct) pmax=parct;
  711.         if ((parct==0)&&(pmax>0)) break;
  712.     }
  713.     if (ct==2) return('g');
  714.     return('b');
  715. }
  716.